package com.zxy.ziems.server.board.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.zxy.ziems.server.board.constant.BoardConstant;
import com.zxy.ziems.server.board.constant.MysqlDateFormatEnum;
import com.zxy.ziems.server.board.pojo.vo.*;
import com.zxy.ziems.server.board.service.EntiretyBoardService;
import com.zxy.ziems.server.classify.pojo.entity.Classify;
import com.zxy.ziems.server.classify.pojo.entity.ClassifyDeviceProperty;
import com.zxy.ziems.server.classify.service.ClassifyDevicePropertyService;
import com.zxy.ziems.server.classify.service.ClassifyService;
import com.zxy.ziems.server.constant.enums.DeviceStatusDictItemEnum;
import com.zxy.ziems.server.constant.enums.DictCodeEnum;
import com.zxy.ziems.server.device.pojo.entity.DataTypeEntity;
import com.zxy.ziems.server.device.pojo.entity.DataUnitEntity;
import com.zxy.ziems.server.device.pojo.entity.DeviceEntity;
import com.zxy.ziems.server.device.pojo.vo.DeviceExtendVO;
import com.zxy.ziems.server.device.service.DataTypeService;
import com.zxy.ziems.server.device.service.DataUnitService;
import com.zxy.ziems.server.device.service.DeviceExtendService;
import com.zxy.ziems.server.device.service.DeviceService;
import com.zxy.ziems.server.domain.DeviceMeasureData;
import com.zxy.ziems.server.domain.DictItem;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataDayMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataHistoryOldMapper;
import com.zxy.ziems.server.energy.service.DeviceMeasureDataService;
import com.zxy.ziems.server.service.DictItemService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import com.zxy.ziems.server.utils.DateUtils;
import com.zxy.ziems.server.vo.ChartVo;
import com.zxy.ziems.server.vo.ChartYAxisVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class EntiretyBoardServiceImpl implements EntiretyBoardService {

    @Autowired
    private DictItemService dictItemService;
    @Autowired
    private ClassifyService classifyService;
    @Autowired
    private DataUnitService dataUnitService;
    @Autowired
    private ClassifyDevicePropertyService classifyDevicePropertyService;
    @Autowired
    private DeviceMeasureDataDayMapper deviceMeasureDataDayMapper;
    @Autowired
    private DeviceMeasureDataService deviceMeasureDataService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private DataTypeService dataTypeService;
    @Autowired
    private DeviceExtendService deviceExtendService;
    @Autowired
    private DeviceMeasureDataHistoryOldMapper deviceMeasureDataHistoryOldMapper;

    @Override
    public CurMonthChangeDosageVo getCurMonth() {
        CurMonthChangeDosageVo res = new CurMonthChangeDosageVo();

        // 获取当月开始时间
        LocalDate monthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 0).toLocalDate();
        LocalDate monthEndDate = DateUtils.getMonthEndDate(LocalDateTime.now(), 0).toLocalDate();
        LocalDate lastMonthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 1).toLocalDate();
        LocalDate lastMonthEndDate = DateUtils.getMonthEndDate(LocalDateTime.now(), 1).toLocalDate();

        Map<String, ChangeDosageVo> monthDosageVoMap = new HashMap<>();
        res.setMonthDosageVoMap(monthDosageVoMap);

        // 获取字典配置分类项
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENTIRETY_BOARD.getCode());

        dictItems.stream().forEach(dictItem -> {
            ChangeDosageVo vo = new ChangeDosageVo();
            Classify classify = classifyService.getById(dictItem.getItemCode());
            vo.setName(classify.getClassifyName());
            vo.setUnit(dataUnitService.getById(classify.getUnitId()).getName());
            BigDecimal val = BigDecimal.ZERO;
            List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
            List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
            AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
            if (CollectionUtil.isEmpty(classifyDeviceProperties)) {
                return;
            }
            val = deviceMeasureDataDayMapper.classifySum(dictItem.getItemCode(), classifyDeviceProperties.get(0).getDataTypeId(), monthStartDate, monthEndDate);
            BigDecimal lastMonthVal = deviceMeasureDataDayMapper.classifySum(dictItem.getItemCode(), classifyDeviceProperties.get(0).getDataTypeId(), lastMonthStartDate, lastMonthEndDate);

            BigDecimal changeRate = null;
            if (lastMonthVal != null && lastMonthVal.compareTo(BigDecimal.ZERO) != 0) {
                changeRate = (val == null ? BigDecimal.ZERO : val).divide(lastMonthVal, 2, RoundingMode.HALF_UP).subtract(BigDecimal.ONE);
            }
            vo.setChangeRate(changeRate == null ? BigDecimal.ZERO : changeRate);
            vo.setValue(val == null ? BigDecimal.ZERO: val);
            monthDosageVoMap.put(dictItem.getItemName(), vo);
        });

        return res;
    }

    @Override
    public Map<String, List<DosageVo>> lastMonthElectricityAndWater() {
        Map<String, List<DosageVo>> res = new HashMap<>();
        // 获取字典配置分类项
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENTIRETY_BOARD.getCode());
        LocalDate lastMonthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 1).toLocalDate();
        LocalDate lastMonthEndDate = DateUtils.getMonthEndDate(LocalDateTime.now(), 1).toLocalDate();

        List<DictItem> electricity = dictItems.stream().filter(m -> !m.getItemName().equals("天燃气")).collect(Collectors.toList());
        for (DictItem dictItem : electricity) {

            List<DosageVo> dosageVos = new ArrayList<>();

            DosageVo vo = new DosageVo();
            Classify classify = classifyService.getById(dictItem.getItemCode());
            vo.setName(classify.getClassifyName());
            vo.setUnit(dataUnitService.getById(classify.getUnitId()).getName());
            List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
            List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
            AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
            if (!CollectionUtil.isEmpty(classifyDeviceProperties)) {
                String dataTypeId = classifyDeviceProperties.get(0).getDataTypeId();
                dosageVos = deviceMeasureDataDayMapper.sumByDateTypeAndClassifyIds(classifyIds, dataTypeId, lastMonthStartDate, lastMonthEndDate, MysqlDateFormatEnum.YM.getFormat());
            }
            res.put(dictItem.getItemName(), dosageVos);
        }

        return res;
    }

    @Override
    public ChartVo lastMonthGas() {
        ChartVo res = new ChartVo();

        List<String> xList = new ArrayList<>();
        List<ChartYAxisVo> yList = new ArrayList<>();

        LocalDate lastMonthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 1).toLocalDate();
        LocalDate lastMonthEndDate = DateUtils.getMonthEndDate(LocalDateTime.now(), 1).toLocalDate();

        List<LocalDate> dateList = DateUtils.getDateDayList(lastMonthStartDate, lastMonthEndDate);
        xList = DateUtils.dateToString(dateList, DateUtils.D_PATTERN);

        // 获取字典配置分类项
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENTIRETY_BOARD.getCode());
        DictItem gas = dictItems.stream().filter(m -> m.getItemName().equals("天燃气")).collect(Collectors.toList()).get(0);

        Classify classify = classifyService.getById(gas.getItemCode());
        ChartYAxisVo chartYAxisVo = new ChartYAxisVo();
        chartYAxisVo.setName(classify.getClassifyName());
        List<String> classifyIds = classifyService.getClassifyId(gas.getItemCode());
        List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
        AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
        List<DosageVo> dosageVos = null;
        if (!CollectionUtil.isEmpty(classifyDeviceProperties)) {
            String dataTypeId = classifyDeviceProperties.get(0).getDataTypeId();
            dosageVos = deviceMeasureDataDayMapper.sumByDateType(gas.getItemCode(), dataTypeId, lastMonthStartDate, lastMonthEndDate, MysqlDateFormatEnum.YM.getFormat());
        }
        List<BigDecimal> data = new ArrayList<>();
        if (dosageVos == null) {
            for (int i = 0; i < xList.size(); i++) {
                data.add(BigDecimal.ZERO);
            }
        } else {
            Map<String, BigDecimal> valMap = dosageVos.stream().collect(Collectors.toMap(DosageVo::getName, DosageVo::getValue));
            for (String s : xList) {
                BigDecimal val = valMap.get(s);
                data.add(val == null ? BigDecimal.ZERO : val);
            }
        }

        chartYAxisVo.setData(data);
        yList.add(chartYAxisVo);

        res.setYAxisList(yList);
        res.setXAxisList(xList);
        return res;
    }

    @Override
    public List<DeviceStatusVo> deviceStatus(DictCodeEnum dictCodeEnum) {
        // 获取配置设备
        List<DictItem> dictItems = dictItemService.getByDictCode(dictCodeEnum.getCode());
        if (CollectionUtil.isEmpty(dictItems)) {
            throw BusinessExceptionUtil.exception("未配置监测设备");
        }
        // 获取颜色配置
        List<DictItem> colorItem = dictItemService.getByDictCode(DictCodeEnum.COLOR.getCode());
        Map<String, String> colerMap = colorItem.stream().collect(Collectors.toMap(DictItem::getItemId, DictItem::getItemCode));

        List<DeviceStatusVo> deviceStatusVos = this.deviceStatus(dictItems.stream().map(DictItem::getItemCode).collect(Collectors.toList()));

        for (DeviceStatusVo deviceStatusVo : deviceStatusVos) {
            List<TargetedValueVo> targetedValues = deviceStatusVo.getTargetedValues();
            Map<String, BigDecimal> map = targetedValues.stream().filter(m -> m.getDateTypeCode().equals(BoardConstant.PRODUCTION_PERCENTAGE))
                    .collect(Collectors.toMap(TargetedValueVo::getDateTypeCode, TargetedValueVo::getValue));
            List<DeviceExtendVO> deviceExtends = deviceStatusVo.getDeviceExtends();
            if (deviceExtends == null) deviceExtends = new ArrayList<>();
            Map<String, String> deviceExtendsMap = deviceExtends.stream().collect(Collectors.toMap(DeviceExtendVO::getDataTypeCode, DeviceExtendVO::getDataTypeVal));
            String colorVal = deviceExtendsMap.get(BoardConstant.DeviceExtendDataCodeEnum.COLOR.getTypeCode());
            if (colorVal != null) {
                String color = colerMap.get(colorVal);
                deviceStatusVo.setColor(color);
            }
            deviceStatusVo.setName(deviceExtendsMap.get(BoardConstant.DeviceExtendDataCodeEnum.PRODUCT_NAME.getTypeCode()));
            BigDecimal productionPercentage = map.get(BoardConstant.PRODUCTION_PERCENTAGE);
            deviceStatusVo.setProductionPercentage(productionPercentage);
        }
        return deviceStatusVos;
    }

    @Override
    public List<DeviceStatusVo> deviceStatus(List<String> deviceIds) {
        List<DeviceStatusVo> res = new ArrayList<>();

        // 获取设备信息
        List<DeviceEntity> deviceList = deviceService.listByIdList(deviceIds);

        // 获取设备所有指标当前值
        List<DeviceMeasureData> currentVal = deviceMeasureDataService.getCurrentValByDeviceIds(deviceIds);
        Map<String, List<DeviceMeasureData>> deviceMap = currentVal.stream().collect(Collectors.groupingBy(DeviceMeasureData::getDeviceId));

        // 获取单位名称
        Set<String> unitIdSet = currentVal.stream().map(DeviceMeasureData::getUnitId).collect(Collectors.toSet());
        List<DataUnitEntity> dataUnitEntities = dataUnitService.listByIdList(unitIdSet);
        Map<String, String> unitNameMap = dataUnitEntities.stream().collect(Collectors.toMap(DataUnitEntity::getId, DataUnitEntity::getName));

        // 获取设备扩展属性
        Map<String, List<DeviceExtendVO>> deviceExtendMap = deviceExtendService.getByDeviceIds(deviceIds);

        // 获取数据类型
        Set<String> typeIdSet = currentVal.stream().map(DeviceMeasureData::getDataTypeId).collect(Collectors.toSet());
        List<DataTypeEntity> dataTypeEntities = dataTypeService.listByIdList(typeIdSet);
        Map<String, DataTypeEntity> typeNameMap = dataTypeEntities.stream().collect(Collectors.toMap(DataTypeEntity::getId, v -> v));

        deviceList.forEach(device -> {
            List<TargetedValueVo> targetedValues = new ArrayList<>();
            List<DeviceMeasureData> deviceMeasureData = deviceMap.get(device.getId());
            if (deviceMeasureData != null) {
                deviceMeasureData.forEach(d -> {
                    DataTypeEntity dataTypeEntity = typeNameMap.get(d.getDataTypeId());
                    TargetedValueVo valueVo = TargetedValueVo.builder()
                            .unitName(unitNameMap.get(d.getUnitId()))
                            .unitId(d.getUnitId())
                            .dataTypeId(d.getDataTypeId())
                            .dataTypeName(dataTypeEntity.getName())
                            .dateTypeCode(dataTypeEntity.getCode())
                            .value(d.getMeasureValue()).build();
                    targetedValues.add(valueVo);
                });
            }

            List<DeviceExtendVO> deviceExtendEntities = deviceExtendMap.get(device.getId());

            DeviceStatusVo vo = DeviceStatusVo.builder()
                    .deviceId(device.getId())
                    .deviceName(device.getName())
                    .targetedValues(targetedValues)
                    .deviceExtends(deviceExtendEntities)
                    .deviceStatus(DeviceStatusDictItemEnum.getOnlineStatusStr(device.getOnlineStatus()))
                    .build();
            res.add(vo);
        });


        return res;
    }

    private BigDecimal getSumVal(List<String> startDate, List<DosageVo> curYearData, List<String> classifyIds, String dataTypeId) {
        BigDecimal res;
        BigDecimal curYearSum = curYearData.stream().filter(m -> startDate.contains(m.getName())).map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 如果全部是当年数据，直接使用当年值即可
        int year1 = LocalDate.now().getYear();
        String year = String.valueOf(year1);
        if (startDate.get(startDate.size() -1).equals(year)) {
            return curYearSum;
        }

        List<String> otherYear = startDate.stream().filter(m -> !m.equals(year)).sorted().collect(Collectors.toList());

        List<DosageVo> dosageVos = deviceMeasureDataHistoryOldMapper.sumByDateTypeAndClassifyIds(classifyIds,
                dataTypeId, DateUtils.toDate(otherYear.get(0) + "-01", DateUtils.YMD_PATTERN),
                DateUtils.getMonthEndDate(DateUtils.toDate(otherYear.get(otherYear.size() -1) + "-01", DateUtils.YMD_PATTERN).atTime(0,0,0), 0).toLocalDate(), MysqlDateFormatEnum.YY_D.getFormat());

        res = dosageVos.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).add(curYearSum);

        return res;
    }

    @Override
    public EnergyVo energyStatistics() {
        EnergyVo res = new EnergyVo();

        // 电
        EnergyClassifyVo electricity = new EnergyClassifyVo();
        List<DosageVo> lastMonthElectricity = new ArrayList<>();
        List<DosageVo> curMonthElectricity = new ArrayList<>();

        // 水
        EnergyClassifyVo water = new EnergyClassifyVo();
        List<DosageVo> lastMonthWater = new ArrayList<>();
        List<DosageVo> curMonthWater = new ArrayList<>();

        // 燃气
        EnergyClassifyVo gas = new EnergyClassifyVo();
        List<DosageVo> lastMonthGas = new ArrayList<>();
        List<DosageVo> curMonthGas = new ArrayList<>();

        // 获取字典配置分类项
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENTIRETY_BOARD.getCode());
        LocalDate lastMonthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 1).toLocalDate();
        LocalDate lastMonthEndDate = DateUtils.getMonthEndCurDate(LocalDateTime.now(), 1).toLocalDate();

        // 获取当月开始时间
        LocalDate monthStartDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 0).toLocalDate();
        LocalDate monthEndDate = DateUtils.getMonthEndCurDate(LocalDateTime.now(), 0).toLocalDate();

        for (DictItem dictItem : dictItems) {
            List<DosageVo> dosageVos = new ArrayList<>();
            List<DosageVo> curDosageVos = new ArrayList<>();

            DosageVo vo = new DosageVo();
            Classify classify = classifyService.getById(dictItem.getItemCode());
            vo.setName(classify.getClassifyName());
            vo.setUnit(dataUnitService.getById(classify.getUnitId()).getName());
            List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
            List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
            AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
            if (!CollectionUtil.isEmpty(classifyDeviceProperties)) {
                String dataTypeId = classifyDeviceProperties.get(0).getDataTypeId();
                dosageVos = deviceMeasureDataDayMapper.sumByDateTypeAndClassifyIds(classifyIds, dataTypeId, lastMonthStartDate, lastMonthEndDate, MysqlDateFormatEnum.YM.getFormat());
                curDosageVos = deviceMeasureDataDayMapper.sumByDateTypeAndClassifyIds(classifyIds, dataTypeId, monthStartDate, monthEndDate, MysqlDateFormatEnum.YM.getFormat());
            }
            BoardConstant.DictItemEntiretyBoard dictItemEntiretyBoard = BoardConstant.DictItemEntiretyBoard.getByName(dictItem.getItemName());
            if (dictItemEntiretyBoard == null) {
                log.warn("能源看板字典配置有误，当前值为{}", dictItem.getItemName());
            }
            switch (dictItemEntiretyBoard) {
                case WATER : lastMonthWater = dosageVos;
                            curMonthWater = curDosageVos;
                    break;
                case GAS : lastMonthGas = dosageVos;
                            curMonthGas = curDosageVos;
                    break;
                case ELECTRICITY : lastMonthElectricity = dosageVos;
                            curMonthElectricity = curDosageVos;
                    break;
            }
        }

        // 计算总量和环比
        BigDecimal lastWaterTotal = lastMonthWater.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);
        BigDecimal lastGasTotal = lastMonthGas.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);
        BigDecimal lastElectricityTotal = lastMonthElectricity.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);
        BigDecimal curWaterTotal = curMonthWater.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);
        BigDecimal curGasTotal = curMonthGas.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);
        BigDecimal curElectricityTotal = curMonthElectricity.stream().map(DosageVo::getValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(0, RoundingMode.HALF_UP);

        BigDecimal electricityRate = null;
        if (lastElectricityTotal != null && lastElectricityTotal.compareTo(BigDecimal.ZERO) != 0) {
            electricityRate = (curElectricityTotal == null ? BigDecimal.ZERO : curElectricityTotal).divide(lastElectricityTotal, 2, RoundingMode.HALF_UP).subtract(BigDecimal.ONE);
        }
        BigDecimal waterRate = null;
        if (lastWaterTotal != null && lastWaterTotal.compareTo(BigDecimal.ZERO) != 0) {
            waterRate = (curWaterTotal == null ? BigDecimal.ZERO : curWaterTotal).divide(lastWaterTotal, 2, RoundingMode.HALF_UP).subtract(BigDecimal.ONE);
        }
        BigDecimal gasRate = null;
        if (lastGasTotal != null && lastGasTotal.compareTo(BigDecimal.ZERO) != 0) {
            gasRate = (curGasTotal == null ? BigDecimal.ZERO : curGasTotal).divide(lastGasTotal, 2, RoundingMode.HALF_UP).subtract(BigDecimal.ONE);
        }
        electricity.setChangeRate(electricityRate == null ? BigDecimal.ZERO : electricityRate);
        electricity.setTotal(curElectricityTotal);
        electricity.setLastMonth(lastMonthElectricity);
        electricity.setCurMonth(curMonthElectricity);
        electricity.setLastTotal(lastElectricityTotal);

        water.setChangeRate(waterRate == null ? BigDecimal.ZERO : waterRate);
        water.setTotal(curWaterTotal);
        water.setLastMonth(lastMonthWater);
        water.setCurMonth(curMonthWater);
        water.setLastTotal(lastWaterTotal);

        gas.setChangeRate(gasRate == null ? BigDecimal.ZERO : gasRate);
        gas.setTotal(curGasTotal);
        gas.setLastMonth(lastMonthGas);
        gas.setCurMonth(curMonthGas);
        gas.setLastTotal(lastGasTotal);

        res.setGas(gas);
        res.setElectricity(electricity);
        res.setWater(water);

        return res;
    }
}
